﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SimioAPI;
using SimioAPI.Extensions;
using System.Collections;

namespace WLC
{
    public class WLCLoadAccountElementDefinition : IElementDefinition
    {
        #region IElementDefinition Members

        public IElement CreateElement(IElementData data)
        {
            return new WLCLoadAccountElement(data);
        }


        public void DefineSchema(IElementSchema schema)
        {
            IPropertyDefinition accountMethod = schema.PropertyDefinitions.AddExpressionProperty("AccountMethod", "0");
            accountMethod.Description = "The method used to calculate workloads (full or load conversion).";
            accountMethod.Required = true;

            IPropertyDefinition releaseMethod = schema.PropertyDefinitions.AddExpressionProperty("ReleaseMethod", "0");
            releaseMethod.Description = "The method used to release orders (WUL, WLB1, WLB2, or WLB3.";
            releaseMethod.Required = true;

            IPropertyDefinition wlcList = schema.PropertyDefinitions.AddElementProperty("WLCWorkstationList", WLCWorkstationListElementDefinition.ID);
            wlcList.Description = "The WLCWorkstationList containing all workstations of the shop floor.";
            wlcList.Required = true;

            IStateDefinition wlcMethodState = schema.StateDefinitions.AddState("wlcMethod");
            wlcMethodState.Description = "Reflects the selected WLC method as a integer number (direct = 0, loadconversion = 1, wlb1 = 2, wlb2 = 3)";
            

        }


        public string Name
        {
            get { return "WLCLoadAccounts"; }
        }

        public string Description
        {
            get { return ""; }
        }

        public System.Drawing.Image Icon
        {
            get { return null; }
        }


       

        public static readonly Guid ID = new Guid("{677F7B76-2648-11DF-88F0-AEA956D89593}");

        public Guid UniqueID
        {
            get { return ID; }
        }

        #endregion
    }


    public class WLCLoadAccountElement : IElement
    {
        
        public String _AppliedReleaseMethod;
        public String _AppliedAccountMethod;
        
        public IPropertyReader _accountMethod;
        public IPropertyReader _releaseMethod;
        public WLCWorkstationListElement _wlcWorkstationList;

        public IState _wlcMethodState;

        public List<OrderWithID> OrderBatchList { get; set; }
        
        // The hashtable to store the account entries in. Uses OrderID as key.
        Hashtable entries;
       

        IElementData _data;

        public WLCLoadAccountElement(IElementData data)
        {
            OrderBatchList = new List<OrderWithID>();
            entries = new Hashtable();
            _accountMethod = data.Properties.GetProperty("AccountMethod");
            _releaseMethod = data.Properties.GetProperty("ReleaseMethod");

            _wlcWorkstationList = (WLCWorkstationListElement) ((IElementProperty) data.Properties.GetProperty("WLCWorkstationList")).GetElement(data.ExecutionContext);

            int releaseMethod = (int)_releaseMethod.GetDoubleValue(data.ExecutionContext);
            int accountMethod = (int)_accountMethod.GetDoubleValue(data.ExecutionContext);

            
            _wlcMethodState = (IState)data.States["wlcMethod"];
            _wlcMethodState.StateValue = releaseMethod;



            switch (releaseMethod)
            {
                case 0:
                    _AppliedReleaseMethod = "wul";
                    break;
                case 1:
                    _AppliedReleaseMethod = "wlb1";
                    break;
                case 2:
                    _AppliedReleaseMethod = "wlb2";
                    break;
                case 3:
                    _AppliedReleaseMethod = "wlb3";
                    break;
                default:
                    _AppliedReleaseMethod = "error";
                    break;
            }

            switch (accountMethod)
            {
                case 0:
                    _AppliedAccountMethod = "full";
                    break;
                case 1:
                    _AppliedAccountMethod = "loadconversion";
                    break;
                default:
                    _AppliedAccountMethod = "error";
                    break;
            }

            _data = data;

        }



        #region IElement Members

        
        public void AdjustWorkloads(int systemOrderID, double orderSize, WLCSequenceElement sequence)
        {

            // If the method applies full load, then only remove the load
            // from the current workstation
            if (_AppliedAccountMethod.Equals("full"))
            {

                // If sequence is in intitial state, the order has not passed any workstation
                // In this case set all workloads for all workstations
                if (sequence.GetCurrentNo() == 0)
                {
                    sequence.SetInitial(orderSize);

                    String currentWorkstationName;
                    double currentStepWorkload;
                    int steps = sequence.GetSequenceCount();

                    

                    for (int i = 0; i < steps; i++)
                    {

                        currentWorkstationName = sequence.GetSequenceWorkstationName(i);
                        if (!currentWorkstationName.Equals("Sink"))
                        {

                            // Get the workload this order produces at the current workstation and multiply it with the order's size
                            currentStepWorkload = sequence.GetSequenceProcessingTime(i) * orderSize;

                            AddEntry(currentWorkstationName, systemOrderID, currentStepWorkload);

                            // Set the current workload for this workstation from the list of workstations
                            _wlcWorkstationList.SetLoadForWorkstation(currentWorkstationName, GetLoadForWorkstation(currentWorkstationName));
                        }
                        
                    }
                    
                }
                else
                {
                    
                    string currentWorkstationName = sequence.GetCurrentWorkstationName();
                    
                    RemoveEntry(currentWorkstationName, systemOrderID);
                    _wlcWorkstationList.SetLoadForWorkstation(currentWorkstationName, GetLoadForWorkstation(currentWorkstationName));
                }
               
            }


            // Load Conversion Method
            else if (_AppliedAccountMethod.Equals("loadconversion") || _AppliedReleaseMethod.Equals("wlb1") || _AppliedReleaseMethod.Equals("wlb2"))
            {
                // Get current workstation name
                string currentWorkstationName = sequence.GetCurrentWorkstationName();

                if (sequence.GetCurrentNo() == 0)
                {
                    sequence.SetInitial(orderSize);

                    // Update workloads of all upstream workstations, becuase we are at release
                    int currentStepNo = sequence.GetCurrentNo();

                    int steps = sequence.GetSequenceCount();
                    double currentStepWorkload;
                    double lpg = 0;
                    double cf = 1;

                   
                    

                    for (int i = 0; i < steps; i++)
                    {

                        currentWorkstationName = sequence.GetSequenceWorkstationName(i);
                        if (!currentWorkstationName.Equals("Sink"))
                        {

                                // Get the workload this order produces at upstream workstation and multiply it with the order's size
                                currentStepWorkload = sequence.GetSequenceProcessingTime(i) * orderSize;
                                                   

                                //System.Windows.Forms.MessageBox.Show("CF: " + cf.ToString() + ", WL: " + currentStepWorkload);

                                // Set the workload for the current upstream workstation
                                AddEntry(currentWorkstationName, systemOrderID, currentStepWorkload * cf);

                                // Set the current workload for this workstation from the list of workstations
                                _wlcWorkstationList.SetLoadForWorkstation(currentWorkstationName, GetLoadForWorkstation(currentWorkstationName));


                                // Get the LoadPercentage of the current workstation and adjust CF
                                lpg = _wlcWorkstationList.GetLoadPercentageForWorkstation(currentWorkstationName);
                                cf = cf * (100 / lpg);
                          

                        }

                    }


                }
                else {

                RemoveEntry(currentWorkstationName, systemOrderID);
                _wlcWorkstationList.SetLoadForWorkstation(currentWorkstationName, GetLoadForWorkstation(currentWorkstationName));


                // Update workloads of all upstream workstations
                int currentStepNo = sequence.GetCurrentNo();

                int steps = sequence.GetSequenceCount();
                double currentStepWorkload;
                double lpg = 0;
                double cf = 1;

                for (int i = 0; i < steps; i++)
                {

                    currentWorkstationName = sequence.GetSequenceWorkstationName(i);
                    if (!currentWorkstationName.Equals("Sink"))
                    {
                        
                        if (sequence.GetSequenceNo(i) > currentStepNo)
                        {

                            // Get the workload this order produces at upstream workstation and multiply it with the order's size
                            currentStepWorkload = sequence.GetSequenceProcessingTime(i) * orderSize;
                          
                                   
                            // Set the workload for the current upstream workstation
                            ChangeEntryValue(currentWorkstationName, systemOrderID, currentStepWorkload * cf);

                            // Set the current workload for this workstation from the list of workstations
                            _wlcWorkstationList.SetLoadForWorkstation(currentWorkstationName, GetLoadForWorkstation(currentWorkstationName));

                            // Get the LoadPercentage of the current workstation
                            lpg = _wlcWorkstationList.GetLoadPercentageForWorkstation(currentWorkstationName);
                            cf = cf * (100 / lpg);
                            
                        }

                    }
                }

                }


            }
          
        }


        private bool AddEntry(string workstationName, int orderID, double value)
        {
            AccountEntries accountEntries;
            if (entries.Contains(workstationName))
            {
                accountEntries = (AccountEntries)entries[workstationName];
                accountEntries.AddEntry(orderID, value);
                return true;

            }
            else {
                accountEntries = new AccountEntries();
                accountEntries.AddEntry(orderID, value);
                entries.Add(workstationName, accountEntries);
                return true;
            }
           
        }


        private double GetEntryValue(string workstationName, int orderID)
        {
            
            if (entries.Contains(workstationName))
            {
                return ((AccountEntries)entries[workstationName]).GetEntryValue(orderID);
            }
            else return 0;
            
        }

        private double GetLoadForWorkstation(string workstationName)
        {
            AccountEntries accountEntries;

            if (entries.Contains(workstationName))
            {
                accountEntries = (AccountEntries)entries[workstationName];
                Hashtable e = accountEntries.GetEntries();
                double sum = 0;
                foreach (int key in e.Keys)
                {
                    sum += (double)e[key];
                }

                return sum;
            }
            else return 0;
        }

        private bool ChangeEntryValue(string workstationName, int orderID, double newValue) {

            if (entries.Contains(workstationName))
            {
                ((AccountEntries)entries[workstationName]).ChangeEntryValue(orderID, newValue);
                return true;

            }
            else return false;

        }

        private bool RemoveEntry(string workstationName, int orderID)
        {
            _data.ExecutionContext.ExecutionInformation.TraceInformation("LOADACCOUNT: Trying to remove order " + orderID + " from load account.");
            
            if (entries.Contains(workstationName))
            {
                AccountEntries e = (AccountEntries)entries[workstationName];
                e.RemoveEntry(orderID);
                return true;

            }
            else return false;


        }


        
        public void Initialize()
        {
                                  
        }

        public void Shutdown()
        {
            // No shutdown code necessary
        }

        #endregion
    }

    public class AccountEntries
    {
        Hashtable entries;

        
        public AccountEntries()
        {
            entries = new Hashtable();
        }

        public void AddEntry(int orderID, double value)
        {
            if (!entries.Contains(orderID))
            {
                entries.Add(orderID, value);
            }
        }

        public void ChangeEntryValue(int orderID, double newValue)
        {
            if (entries.Contains(orderID))
            {
                entries[orderID] = newValue;
            }
            else
                AddEntry(orderID, newValue);
        }

        public double GetEntryValue(int orderID)
        {
            if (entries.Contains(orderID))
            {
                return (double) entries[orderID];
            }
            else return 0;

        }

        public bool RemoveEntry(int orderID)
        {
            if (entries.Contains(orderID))
            {
                entries.Remove(orderID);
                return true;
            }
            else return false;

        }

        public Hashtable GetEntries()
        {
            return entries;
        }

    }
    


}

